home *** CD-ROM | disk | FTP | other *** search
/ Light ROM 1 / LIGHT-ROM 1 (Amiga Library Services)(1994).iso / ffdisks / d952.lha / UUArc / UUArc.c < prev    next >
C/C++ Source or Header  |  1993-11-22  |  41KB  |  1,304 lines

  1. /*                                                          */
  2. /*                  UUARC V1.3 04/10/93                     */
  3. /*                      J.G.Brandon                         */
  4. /*                                                          */
  5. /*                                                          */
  6. /*  Archiving system based on the 'UU' encoding/decoding    */
  7. /*  algorythms.                                             */
  8. /*                                                          */
  9. /*  Written in 'C' for the public domain using NorthC V1.3  */
  10. /*  on a Commodore Amiga.  Ought to compile/run under most  */
  11. /*  compilers/environments.                                 */
  12. /*                                                          */
  13. /*  No system specific commands/libraries/includes/headers  */
  14. /*  used to make 'C' source portable.  The only system      */
  15. /*  specific part of the program is the pre-processor       */
  16. /*  defined function to identify 'directory separator'      */
  17. /*  characters, used to separate file-names from their      */
  18. /*  paths.                                                  */
  19. /*                                                          */
  20.  
  21.  
  22.  
  23. /*  If required, comment the next line to stop checksums being added    */
  24. /*  (This only effects encoding output, decoding will still check       */
  25. /*  checksums if checksums are present)                                 */
  26. #define ADDCHECK
  27.  
  28.  
  29.  
  30. /*  Title/version definitions   */
  31.  
  32. #define TITLE       "UUArc"
  33. #define AUTHOR      "Miss J.G.Brandon"
  34. #define VERSION     1
  35. #define REVISION    3
  36. #define TITLESTR    "\n%s Version %d.%d by %s %s.\n\n"
  37. #define TITLESTRPAR TITLE, VERSION, REVISION, AUTHOR, __DATE__
  38.  
  39.  
  40.  
  41. /*  Required standard libraries */
  42.  
  43. #include    <stdio.h>
  44. #include    <ctype.h>
  45. #include    <string.h>
  46. #include    <stdlib.h>
  47. #include    <signal.h>
  48.  
  49. /*  When are standard includes not standard includes?       */
  50. /*  When they miss out definitions that should be there.    */
  51. /*  I'll name no names; but it's a very popular compiler... */
  52. /*  (VERY naughty, as it sets the _STDC_ flag to indicate   */
  53. /*   that it is completely ANSI standard conforming;        */
  54. /*   and these are just the ones missed out that this       */
  55. /*   program needs, there are many more.)                   */
  56.  
  57. #ifndef EXIT_SUCCESS
  58. #define EXIT_SUCCESS        0
  59. #endif
  60.  
  61. #ifndef EXIT_FAILURE
  62. #define EXIT_FAILURE        20
  63. #endif
  64.  
  65. #ifndef L_tmpnam
  66. #define L_tmpnam            0x0FF
  67. #endif
  68.  
  69.  
  70. /*  Define conditional close function; attempts to close a file indicated   */
  71. /*  by the supplied file pointer if the pointer is not null                 */
  72.  
  73. #define COND_CLOSE(fptr)    if (fptr != NULL) fclose(fptr)
  74.  
  75.  
  76. /*  Define 'directory-separator' identifier - system specific unfortunately */
  77.  
  78. #define DIR_CHAR(c)         ((c == ':') || (c == '/'))
  79.  
  80.  
  81. /*  Define uuencoding/decoding/checking functions   */
  82.  
  83. #define VALIDATE(c)         ((c < 0x020) || (c > 0x060))
  84. #define DECODE(c)           ((c - 0x020) & 0x03F)
  85. #define ENCODE(c)           (c ? ((c & 0x03F) + 0x020) : 0x060)
  86.  
  87.  
  88. /*  Definitions required by program */
  89.  
  90. #define LISTCOM             'l'
  91. #define TESTCOM             't'
  92. #define EXTRACTCOM          'x'
  93. #define DELETECOM           'd'
  94. #define ADDCOM              'a'
  95. #define MOVEOPT             'm'
  96. #define PATHOPT             'p'
  97.  
  98. #define BUFSIZE             0x0FF
  99.  
  100. #define ENCODESIZE          45
  101. #define DEFAULTMODE         0744
  102.  
  103. #define NOFILE              0
  104. #define FOUNDFILE           1
  105.  
  106. #define DATALINE            0
  107. #define BEGINLINE           1
  108. #define ENDLINE             2
  109. #define SIZELINE            3
  110. #define BADLINE             4
  111.  
  112. #define STRIPPATH           0
  113. #define LEAVEPATH           1
  114.  
  115. #define LEAVEFILE           0
  116. #define MOVEFILE            1
  117.  
  118. #define SHOWUSAGE           0
  119. #define NOUSAGE             1
  120.  
  121. #define DUNNO               0
  122. #define YES                 1
  123. #define NO                  2
  124.  
  125. #define CHECKSIZE           64
  126.  
  127.  
  128.  
  129. /*  If this is ANSI 'C', do function prototyping    */
  130.  
  131. #ifdef  _STDC_
  132. char *prname(char *, int);
  133. int anycasecmp(char *, char *)
  134. int filecmp(char *, char *[], int)
  135. void pr_head(char *)
  136. void pr_tail(void)
  137. void operout(char *, int)
  138. void succout(char *, int)
  139. void errsout(long)
  140. void usage(FILE *)
  141. void cleanexit(int)
  142. void errorexit(char *, int)
  143. void abnormalexit(int)
  144. int listarc(void)
  145. int deletearc(char *, char *[], int)
  146. int extractarc(char, char *[], int)
  147. int fileread(char *)
  148. int addarc(char *[], int)
  149. #endif
  150.  
  151.  
  152.  
  153. /*  Globally accessable archive, file and temporary file details    */
  154.  
  155. FILE *arc_fptr = NULL, *file_fptr = NULL, *temp_fptr = NULL;
  156. char temp_name[L_tmpnam] = "";
  157.  
  158. /*  Globally accessable number of errors counter    */
  159.  
  160. long numerrors = 0;
  161.  
  162. /*  Globally accessable path strip flag */
  163.  
  164. int pathflag = STRIPPATH;
  165.  
  166. /*  Globally accessable move files flag */
  167.  
  168. int moveflag = LEAVEFILE;
  169.  
  170.  
  171.  
  172. /*  Strip path name from a filename if required */
  173.  
  174. char *prname(filename, flag)
  175. char *filename;
  176. int flag;
  177. {
  178.     char *stripname;
  179.  
  180.     /*  If full path has been requested, leave it in    */
  181.     if (flag == LEAVEPATH)
  182.         return filename;
  183.  
  184.     /*  Hunt from left to right for a path-indicator character  */
  185.     for(stripname = filename+strlen(filename);
  186.         ((stripname > filename) && (!DIR_CHAR(*stripname)));
  187.         stripname--)
  188.         ;
  189.  
  190.     /*  If path-indicator character found, increment pointer to */
  191.     /*  point to start of file name                             */
  192.     if DIR_CHAR(*stripname)
  193.         stripname++;
  194.  
  195.     /*  Return pointer to filename without path extension   */
  196.     return stripname;
  197. }
  198.  
  199.  
  200.  
  201. /*  String comparison, ignoring case - result same as strcmp */
  202.  
  203. int anycasecmp(string1, string2)
  204. char *string1, *string2;
  205. {
  206.     int i;
  207.     for (i = 0; toupper(string1[i]) == toupper(string2[i]); i++)
  208.         if (string1[i] == '\0')
  209.             return 0;
  210.     return toupper(string1[i]) - toupper(string2[i]);
  211. }
  212.  
  213.  
  214.  
  215. /*  Check filename against list of given filenames   */
  216.  
  217. int filecmp(filename, argv, argc)
  218. char *filename;
  219. char *argv[];
  220. int argc;
  221. {
  222.     int targc;
  223.     int filematch = NOFILE;
  224.     if (argc == 3)
  225.         filematch = FOUNDFILE;
  226.     else
  227.         for (targc = 3; targc < argc; targc++)
  228.             if (anycasecmp(prname(filename, pathflag),
  229.                 prname(argv[targc], pathflag)) == 0)
  230.                 filematch = FOUNDFILE;
  231.     return filematch;
  232. }
  233.  
  234.  
  235.  
  236. /*  Print archive header information    */
  237.  
  238. void pr_head(archive)
  239. char *archive;
  240. {
  241.     printf(" archive '%s':\n", archive);
  242.     printf("---------------------------------------------\n");
  243. }
  244.  
  245.  
  246.  
  247. /*  Print archive tail information  */
  248.  
  249. void pr_tail()
  250. {
  251.     printf("---------------------------------------------\n");
  252. }
  253.  
  254.  
  255.  
  256. /*  Outputs details of an operation, taking plurals into account    */
  257.  
  258. void operout(operstr,numfiles)
  259. char *operstr;
  260. int numfiles;
  261. {
  262.     if (numfiles == 0)
  263.         printf("No ");
  264.     else
  265.         printf("%d ",numfiles);
  266.  
  267.     if (numfiles != 1)
  268.         printf("files ");
  269.     else
  270.         printf("file ");
  271.  
  272.     printf("%s.\n",operstr);
  273. }
  274.  
  275.  
  276.  
  277. /*  Outputs details of success of an operation  */
  278.  
  279. void succout(operstr, numerrs)
  280. char *operstr;
  281. int numerrs;
  282. {
  283.     if (numerrs == 0)
  284.         printf("%s successful.\n\n",operstr);
  285.     else
  286.         fprintf(stderr,"%s not entirely successful.\n\n",operstr);
  287. }
  288.  
  289.  
  290.  
  291. /*  Outputs details of errors, taking plurals into account  */
  292.  
  293. void errsout(numerrs)
  294. long numerrs;
  295. {
  296.     if (numerrs != 0) {
  297.         fprintf(stderr, "%d error", numerrs);
  298.         if (numerrs != 1)
  299.             fprintf(stderr, "s.\n");
  300.         else
  301.             fprintf(stderr, ".\n");
  302.     }
  303. }
  304.  
  305.  
  306.  
  307. /*  Sends details of command usage to output stream 'ostrm'  */
  308.  
  309. void usage(ostrm)
  310. FILE *ostrm;
  311. {
  312.     fprintf(ostrm,
  313.         "\nUSAGE:\t%s -<command>[%c][%c] <archive> [<filename> ... ]\n",
  314.         TITLE, PATHOPT, MOVEOPT);
  315.  
  316.     fprintf(ostrm, "\nWhere <command> is one of-\n");
  317.     fprintf(ostrm, "\t%c = List contents of <archive>.\n", LISTCOM);
  318.     fprintf(ostrm, "\t%c = Test contents of <archive>.\n", TESTCOM);
  319.     fprintf(ostrm, "\t%c = Add <filename(s)> to <archive>.\n", ADDCOM);
  320.     fprintf(ostrm, "\t%c = Extract <filenames> from <archive>.\n", EXTRACTCOM);
  321.     fprintf(ostrm,  "\t    (All files if no <filenames> given.)\n");
  322.     fprintf(ostrm, "\t%c = Delete <filenames> from <archive>.\n", DELETECOM);
  323.     fprintf(ostrm,  "\t    (All files if no <filenames> given.)\n");
  324.  
  325.     fprintf(ostrm,
  326.         "\nIf included after the archiver command, the '%c' option\n",
  327.         PATHOPT);
  328.     fprintf(ostrm,
  329.         "specifies full path names to be considered; otherwise path\n");
  330.     fprintf(ostrm,
  331.         "names will be ignored.\n");
  332.  
  333.     fprintf(ostrm,
  334.         "\nIf included after an add/extract archiver command, the '%c'\n",
  335.         MOVEOPT);
  336.     fprintf(ostrm,
  337.         "option specifies files to be moved from source, i.e. source\n");
  338.     fprintf(ostrm,
  339.         "files will be deleted; otherwise they are left as is.\n");
  340.  
  341.     fprintf(ostrm,
  342.         "\nIf applicable, <archive> must include the '.uue' extension.\n\n");
  343. }
  344.  
  345.  
  346.  
  347. /*  A 'clean' exit routine; closes any open files, deletes the temporary    */
  348. /*  file if used, and exits with return code 'retcode'                      */
  349.  
  350. void cleanexit(retcode)
  351. int retcode;
  352. {
  353.     COND_CLOSE(arc_fptr);
  354.     COND_CLOSE(file_fptr);
  355.     COND_CLOSE(temp_fptr);
  356.     if (strcmp(temp_name,"") != 0)
  357.         remove(temp_name);
  358.  
  359.     exit(retcode);
  360. }
  361.  
  362.  
  363.  
  364. /*  Exit routine for errors; displays error 'errorstr' and command usage    */
  365. /*  to the standard error output stream, then 'cleanexit's with a failure   */
  366. /*  return code                                                             */
  367.  
  368. void errorexit(errorstr, usageflag)
  369. char *errorstr;
  370. int usageflag;
  371. {
  372.     fprintf(stderr, "\n\aERROR: %s.\n\n", errorstr);
  373.  
  374.     if (usageflag == SHOWUSAGE)
  375.         usage(stderr);
  376.  
  377.     cleanexit(EXIT_FAILURE);
  378. }
  379.  
  380.  
  381.  
  382. /*  Deal with abnormal program termination */
  383.  
  384. void abnormalexit(sig)
  385. int sig; 
  386. {
  387.     fprintf(stderr, "\n\aERROR: Program aborted.\n\n");
  388.     cleanexit(EXIT_FAILURE);
  389. }
  390.  
  391.  
  392.  
  393. /*  List files in the opened archive    */
  394.  
  395. int listarc()
  396. {
  397.     char buffer[BUFSIZE], filestr[BUFSIZE];
  398.     int filemode, numfiles = 0;
  399.  
  400.     /*  Go through archive until error/end-of-file, listing any files found */
  401.  
  402.     while(fgets(buffer, BUFSIZE, arc_fptr) != NULL) {
  403.         if (sscanf(buffer, "begin %o %s", &filemode, filestr) == 2) {
  404.             numfiles++;
  405.             printf("Found                 - %s\n", filestr);
  406.         }
  407.     }
  408.  
  409.     return numfiles;
  410. }
  411.  
  412.  
  413.  
  414. /*  Delete files from an archive    */
  415.  
  416. int deletearc(arcname, argv, argc)
  417. char *arcname;
  418. char *argv[];
  419. int argc;
  420. {
  421.     char buffer[BUFSIZE], filestr[BUFSIZE], arcfile[BUFSIZE];
  422.     int filemode, numdel = 0, fileflag = NOFILE;
  423.     int deleteflag = NOFILE, lastdelete = NOFILE;
  424.     int linetype, lastline = DATALINE;
  425.     long expectedsize;
  426.  
  427.  
  428.     /*  Open archive for input, and a temporary file for output/input   */
  429.  
  430.     if (arc_fptr == NULL)
  431.         if ((arc_fptr = fopen(arcname, "r")) == NULL)
  432.             errorexit("Can't open archive for input", SHOWUSAGE);
  433.  
  434.     if ((temp_fptr = fopen((tmpnam(temp_name)), "w+")) == NULL)
  435.         errorexit("Can't open temporary file", NOUSAGE);
  436.  
  437.  
  438.     /*  Deal with each line in archive until end-of-file/error  */
  439.  
  440.     while(fgets(buffer, BUFSIZE, arc_fptr) != NULL) {
  441.  
  442.  
  443.         /*  Signal a data line by default   */
  444.         linetype = DATALINE;
  445.  
  446.  
  447.         /*  Deal with a begin statement */
  448.  
  449.         if (sscanf(buffer, "begin %o %s", &filemode, filestr) == 2) {
  450.  
  451.             /*  Indicate command line */
  452.             linetype = BEGINLINE;
  453.  
  454.             /*  If already dealing with a file, error - missing end */
  455.             if (fileflag == FOUNDFILE) {
  456.                 fprintf(stderr, "Missing 'end'         - %s\n", arcfile);
  457.                 numerrors++;
  458.                 printf("Deleted               - %s\n", arcfile);
  459.                 numdel++;
  460.             }
  461.  
  462.             /*  Remember file name, signal that a file's been found */
  463.             strcpy(arcfile, filestr);
  464.             fileflag = FOUNDFILE;
  465.  
  466.             /*  Store previous delete status    */
  467.             lastdelete = deleteflag;
  468.  
  469.             /*  Signal whether or not file is to be deleted */
  470.             deleteflag = filecmp(arcfile, argv, argc);
  471.  
  472.         }
  473.  
  474.  
  475.         /*  Deal with a size statement */
  476.  
  477.         if (sscanf(buffer, "size %d", &expectedsize) == 1) {
  478.  
  479.             /*  Indicate command line */
  480.             linetype = SIZELINE;
  481.  
  482.             /*  A size statement is only expected after an end statement    */
  483.             if (lastline != ENDLINE) {
  484.                 fprintf(stderr, "Misplaced 'size'      - %s\n", arcfile);
  485.                 numerrors++;
  486.             }
  487.         }
  488.  
  489.  
  490.         /*  Copy lines not to be deleted from archive to temporary file */
  491.  
  492.         if (!((deleteflag == FOUNDFILE) ||
  493.              ((lastline == ENDLINE) &&
  494.               (linetype == SIZELINE) &&
  495.               (lastdelete == FOUNDFILE))))
  496.             if (fputs(buffer, temp_fptr) == EOF)
  497.                 errorexit("File error writing to temporary file", NOUSAGE);
  498.  
  499.  
  500.         /*  Deal with an end statement  */
  501.  
  502.         if (strncmp(buffer, "end", 3) == 0) {
  503.             
  504.             /*  Indicate command line */
  505.             linetype = ENDLINE;
  506.  
  507.             /*  An end statement; so make sure we had a start statement */
  508.             if (fileflag == FOUNDFILE) {
  509.                 /*  Signal that file has been processed, and increment  */
  510.                 /*  number of files deleted counter if necessary        */
  511.                 if (deleteflag == FOUNDFILE) {
  512.                     printf("Deleted               - %s\n", arcfile);
  513.                     numdel++;
  514.                 }
  515.                 else
  516.                     printf("Skipped               - %s\n", arcfile);
  517.             }
  518.             else {
  519.                 /*  If no relevant start statement, error - missing begin   */
  520.                 fprintf(stderr, "Missing 'begin'       - ?\n");
  521.                 numerrors++;
  522.             }
  523.  
  524.             /*  Store previous delete status    */
  525.             lastdelete = deleteflag;
  526.  
  527.             /*  Clear deletion and processing a file    */
  528.             deleteflag = NOFILE;
  529.             fileflag = NOFILE;
  530.  
  531.         }
  532.  
  533.         lastline = linetype;
  534.  
  535.     }
  536.  
  537.     /*  If we hit end of archive file whilst still expecting data, error    */
  538.     if (fileflag != NOFILE) {
  539.         fprintf(stderr, "Missing (final) 'end' - %s\n", arcfile);
  540.         numerrors++;
  541.         printf("Deleted               - %s\n", arcfile);
  542.         numdel++;
  543.     }
  544.  
  545.  
  546.     /*  Go back to start of temporary file  */
  547.     rewind(temp_fptr);
  548.  
  549.     /*  Close archive file, and re-open it (cleared) for output */
  550.     COND_CLOSE(arc_fptr);
  551.     if ((arc_fptr = fopen(arcname, "w")) == NULL)
  552.         errorexit("Can't open archive for output", SHOWUSAGE);
  553.  
  554.     /*  Copy contents of temporary file to archive file */
  555.     while(fgets(buffer, BUFSIZE, temp_fptr) != NULL)
  556.         if (fputs(buffer, arc_fptr) == EOF)
  557.             errorexit("File error writing to archive file", NOUSAGE);
  558.  
  559.  
  560.     /*  Return number of files deleted  */
  561.     return numdel;
  562. }
  563.  
  564.  
  565.  
  566. /*  Extract/Test files in the opened archive    */
  567.  
  568. int extractarc(extcom, argv, argc)
  569. char extcom;
  570. char *argv[];
  571. int argc;
  572. {
  573.     char buffer[BUFSIZE], filestr[BUFSIZE], arcfile[BUFSIZE], outstr[BUFSIZE];
  574.     int ic1, ic2, ic3, ic4;
  575.     int filemode, numfiles = 0, fileflag = NOFILE;
  576.     unsigned int checksum;
  577.     int checkflag;
  578.     int linetype, lastline = DATALINE, numdata, datapos, linelen;
  579.     long filesize = 0, expectsize;
  580.  
  581.  
  582.     /*  Go through archive until error/end-of-file  */
  583.  
  584.     while(fgets(buffer, BUFSIZE, arc_fptr) != NULL) {
  585.  
  586.  
  587.         /*  As a default, assume line to be a line of UUencoded data    */
  588.  
  589.         linetype = DATALINE;
  590.  
  591.  
  592.         /*  Deal with a begin statement */
  593.  
  594.         if (sscanf(buffer, "begin %o %s", &filemode, filestr) == 2) {
  595.  
  596.             /*  Flag line as being a command line   */
  597.             linetype = BEGINLINE;
  598.  
  599.             /*  If already dealing with a file, error - missing end */
  600.             if (fileflag == FOUNDFILE) {
  601.                 fprintf(stderr, "Missing 'end'         - %s\n", arcfile);
  602.                 numerrors++;
  603.                 COND_CLOSE(file_fptr);
  604.                 file_fptr = NULL;
  605.                 /*  If just testing archive, indicate that file has been    */
  606.                 /*  processed and increment number of files counter         */
  607.                 if (extcom == TESTCOM) {
  608.                     printf("%21d - %s\n", filesize, arcfile);
  609.                     numfiles++;
  610.                 }
  611.                 else {
  612.                     /*  If extracting, indicate that file has been      */
  613.                     /*  processed and increment number of files counter */
  614.                     /*  if necessary                                    */
  615.                     if (file_fptr != NULL) {
  616.                         printf("%21d - %s\n", filesize, arcfile);
  617.                         numfiles++;
  618.                     }
  619.                     else
  620.                         printf("Skipped               - %s\n", arcfile);
  621.                 }
  622.             }
  623.  
  624.             /*  Remember file name, signal that a file's been found,    */
  625.             /*  set file size counter to 0, and reset checksum status   */
  626.             strcpy(arcfile, filestr);
  627.             fileflag = FOUNDFILE;
  628.             filesize = 0;
  629.             checkflag = DUNNO;
  630.  
  631.             /*  If not just testing the archive, open file for output   */
  632.             if ((extcom != TESTCOM) && 
  633.                 (filecmp(arcfile, argv, argc) == FOUNDFILE))
  634.                 if ((file_fptr = fopen(prname(arcfile,
  635.                     pathflag), "w")) == NULL) {
  636.                     fprintf(stderr, "Can't open for output - %s\n",
  637.                         prname(arcfile, pathflag));
  638.                     numerrors++;
  639.                 }
  640.         }
  641.  
  642.  
  643.         /*  Deal with an end statement  */
  644.  
  645.         if (strncmp(buffer, "end", 3) == 0) {
  646.             
  647.             /*  Flag line as being a command line   */
  648.             linetype = ENDLINE;
  649.  
  650.             /*  An end statement; so make sure we had a start statement */
  651.             if (fileflag == FOUNDFILE) {
  652.                 /*  If just testing archive, indicate that file has been    */
  653.                 /*  processed and increment number of files counter         */
  654.                 fileflag = NOFILE;
  655.                 if (extcom == TESTCOM) {
  656.                     printf("%21d - %s\n", filesize, arcfile);
  657.                     numfiles++;
  658.                 }
  659.                 else {
  660.                     /*  If extracting, indicate that file has been      */
  661.                     /*  processed and increment number of files counter */
  662.                     /*  if necessary                                    */
  663.                     if (file_fptr != NULL) {
  664.                         printf("%21d - %s\n", filesize, arcfile);
  665.                         numfiles++;
  666.                     }
  667.                     else
  668.                         printf("Skipped               - %s\n", arcfile);
  669.                 }
  670.             }
  671.             else {
  672.                 /*  If no relevant start statement, error - missing begin   */
  673.                 fprintf(stderr, "Missing 'begin'       - ?\n");
  674.                 numerrors++;
  675.             }
  676.  
  677.             COND_CLOSE(file_fptr);
  678.             file_fptr = NULL;
  679.  
  680.         }
  681.  
  682.  
  683.         /*  Deal with a size statement  */
  684.  
  685.         if (sscanf(buffer, "size %d", &expectsize) == 1) {
  686.  
  687.             /*  Flag line as being a command line   */
  688.             linetype = SIZELINE;
  689.  
  690.             /*  A size statement is only expected after an end statement    */
  691.             if (lastline != ENDLINE) {
  692.                 fprintf(stderr, "Misplaced 'size'      - %s\n", arcfile);
  693.                 numerrors++;
  694.             }
  695.             else {
  696.                 /*  Check the size of the file was as expected  */
  697.                 if ((filesize != expectsize) &&
  698.                     ((extcom == TESTCOM) || (file_fptr != NULL))) {
  699.                     fprintf(stderr, "File size wrong       - %s\n", arcfile);
  700.                     numerrors++;
  701.                 }
  702.             }
  703.         }
  704.  
  705.  
  706.         /*  Deal with a line of data    */
  707.  
  708.         if (linetype == DATALINE) {
  709.  
  710.             /*  Only want data lines within begin/end statements    */
  711.             /*  And only want to bother decoding file if required   */
  712.             if ((fileflag == FOUNDFILE) &&
  713.                 ((extcom == TESTCOM) || (file_fptr != NULL))) {
  714.  
  715.                 /*  Strip any white-space characters from end of line   */
  716.                 /*  that aren't pure space characters                   */
  717.                 if ((datapos = strlen(buffer)-1) >= 0)
  718.                     while (isspace(buffer[datapos]) &&
  719.                             (buffer[datapos] != ' ') &&
  720.                             (datapos >= 0)) {
  721.                         buffer[datapos] = '\0';
  722.                         datapos--;
  723.                     }
  724.  
  725.                 linelen=strlen(buffer);
  726.  
  727.                 /*  If any data is in the line  */
  728.                 if (linelen > 0) {
  729.  
  730.                     /*  Reset line checksum */
  731.                     checksum = 0;
  732.  
  733.                     /*  Error if any characters not within limits   */
  734.                     for(datapos = 0;
  735.                         ((datapos < linelen) && (linetype == DATALINE));
  736.                         datapos++)
  737.                         if VALIDATE(buffer[datapos]) {
  738.                             fprintf(stderr,
  739.                                 "Corrupt byte in line  - %s\n", arcfile);
  740.                             numerrors++;
  741.                             linetype = BADLINE;
  742.                         }
  743.  
  744.  
  745.                     /*  Attempt to decode line  */
  746.                     if (linetype == DATALINE) {
  747.  
  748.                         /*  Fetch total number of bytes to written out  */
  749.                         numdata = DECODE(*buffer);
  750.                         /*  Decode relevant number of bytes from archive    */
  751.                         for(datapos = 1;
  752.                             (((datapos+numdata)<linelen) && (numdata>0));
  753.                             datapos+=4) {
  754.                             ic1 = DECODE(buffer[datapos]);
  755.                             ic2 = DECODE(buffer[datapos+1]);
  756.                             ic3 = DECODE(buffer[datapos+2]);
  757.                             ic4 = DECODE(buffer[datapos+3]);
  758.                             outstr[numdata+2] = ic1 << 2 | ic2 >> 4;
  759.                             outstr[numdata+1] = ic2 << 4 | ic3 >> 2;
  760.                             outstr[numdata] =   ic3 << 6 | ic4     ;
  761.                             checksum = ( checksum +
  762.                                          outstr[numdata+2] +
  763.                                          outstr[numdata+1] +
  764.                                          outstr[numdata]     ) % CHECKSIZE;
  765.                             numdata-=3;  
  766.                         }
  767.  
  768.                         /*  Error if not enough data in line    */
  769.                         if ((numdata > 0) || ((checkflag == YES) &&
  770.                             ((linelen-datapos) < 1))) {
  771.                             fprintf(stderr,
  772.                                 "Data line too short   - %s\n", arcfile);
  773.                             numerrors++;
  774.                             linetype = BADLINE;
  775.                         }
  776.  
  777.                         /*  If line is okay, and checksums have been        */
  778.                         /*  present, then analyse checksum                  */
  779.                         if ((checkflag == YES) && (linetype == DATALINE)) {
  780.                             if (checksum != DECODE(buffer[datapos])) {
  781.                                 fprintf(stderr,
  782.                                     "Checksum error        - %s\n", arcfile);
  783.                                 numerrors++;
  784.                                 linetype = BADLINE;
  785.                             }
  786.                         }
  787.  
  788.                         /*  Error if too much data in line - allow extra    */
  789.                         /*  pure space characters though                    */
  790.                         if (((checkflag != NO) && ((linelen-datapos) > 2)) ||
  791.                             ((checkflag == NO) && ((linelen-datapos) > 1))) {
  792.                             if (checkflag == NO)
  793.                                 datapos++;
  794.                             else
  795.                                 datapos=datapos + 2;
  796.                             while((datapos<linelen)
  797.                                 && (linetype != BADLINE))
  798.                                 if (buffer[datapos++] != ' ')
  799.                                     linetype = BADLINE;
  800.                             if (linetype == BADLINE) {
  801.                                 fprintf(stderr,
  802.                                     "Data line too long    - %s\n", arcfile);
  803.                                 numerrors++;
  804.                             }
  805.                         }
  806.  
  807.                         /*  If the line was okay, and file checksum status  */
  808.                         /*  is still unknown, then establish the status     */
  809.                         if ((checkflag == DUNNO) && (linetype == DATALINE)) {
  810.                             if ((linelen-datapos) == 0)
  811.                                 checkflag = NO;
  812.                             else {
  813.                                 if (checksum == DECODE(buffer[datapos])) {
  814.                                     checkflag = YES;
  815.                                 }
  816.                                 else {
  817.                                     fprintf(stderr,
  818.                                         "Bad checksum/linesize - %s\n",
  819.                                         arcfile);
  820.                                     numerrors++;
  821.                                     linetype = BADLINE;
  822.                                 }
  823.                             }
  824.                         }
  825.                     }
  826.  
  827.                     /*  If line is completely error free    */
  828.                     if (linetype == DATALINE) {
  829.                         /*  Increment file size counter */
  830.                         filesize += (numdata = DECODE(*buffer));
  831.                         /*  If file has been opened for output, write out   */
  832.                         /*  decoded bytes                                   */
  833.                         if (file_fptr != NULL)
  834.                             while(numdata > 0) {
  835.                                 if (fputc(outstr[numdata+2],
  836.                                     file_fptr) == EOF)
  837.                                     errorexit("File error writing file",
  838.                                         NOUSAGE);
  839.                                 numdata--;
  840.                             }
  841.                     }
  842.  
  843.                 }
  844.  
  845.                 else {
  846.                     fprintf(stderr, "No data, empty line   - %s\n", arcfile);
  847.                     numerrors++;
  848.                 }
  849.  
  850.             }
  851.  
  852.         }
  853.  
  854.         lastline = linetype;
  855.  
  856.     }
  857.  
  858.     /*  If we hit end of archive file whilst still expecting data, error    */
  859.     if (fileflag != NOFILE) {
  860.         fprintf(stderr, "Missing (final) 'end' - %s\n", arcfile);
  861.         numerrors++;
  862.         /*  If just testing archive, indicate that file has been    */
  863.         /*  processed and increment number of files counter         */
  864.         if (extcom == TESTCOM) {
  865.             printf("%21d - %s\n", filesize, arcfile);
  866.             numfiles++;
  867.         }
  868.         else {
  869.             /*  If extracting, indicate that file has been      */
  870.             /*  processed and increment number of files counter */
  871.             /*  if necessary                                    */
  872.             if (file_fptr != NULL) {
  873.                 printf("%21d - %s\n", filesize, arcfile);
  874.                 numfiles++;
  875.             }
  876.             else
  877.                 printf("Skipped               - %s\n", arcfile);
  878.         }
  879.     }
  880.  
  881.     /*  Return number of files extracted    */
  882.     return numfiles;
  883. }
  884.  
  885.  
  886.  
  887. /*  Read from file, dealing with with any errors    */
  888.  
  889. int fileread(buf)
  890. char *buf;
  891. {
  892.     int numread;
  893.  
  894.     /*  Read data into buffer   */
  895.     numread = fread(buf, sizeof(char), ENCODESIZE, file_fptr);
  896.  
  897.     /*  If an error occured, exit cleanly with an error */
  898.     if ferror(file_fptr)
  899.         errorexit("File error reading file", NOUSAGE);
  900.  
  901.     /*  Return number of bytes read */
  902.     return numread;
  903. }
  904.  
  905.  
  906.  
  907. /*  Add files specified to the opened archive   */
  908.  
  909. int addarc(argv, argc)
  910. char *argv[];
  911. int argc;
  912. {
  913.     char buffer[BUFSIZE], outstr[BUFSIZE];
  914.     int ic1, ic2, ic3, oc1, oc2, oc3, oc4;
  915.     int inpos, outpos, targc, numfiles = 0, n;
  916.     long filesize;
  917.     #ifdef  ADDCHECK
  918.     unsigned int checksum;
  919.     #endif
  920.  
  921.     /*  Repeat for each filename given  */
  922.     for (targc = 3; targc < argc; targc++) {
  923.  
  924.         /*  Reset filesize counter  */
  925.         filesize = 0;
  926.  
  927.         /*  Open file for input */
  928.         if ((file_fptr = fopen(argv[targc], "r")) == NULL) {
  929.             fprintf(stderr, "Can't open for input  - %s\n", argv[targc]);
  930.             numerrors++;
  931.         }
  932.         else {
  933.  
  934.             /*  Write begin statement   */
  935.             fprintf(arc_fptr, "\nbegin %o %s\n",
  936.                 DEFAULTMODE, prname(argv[targc], pathflag));
  937.  
  938.             /*  Read and encode from file whilst data is available  */
  939.  
  940.             do {
  941.  
  942.                 n = fileread(buffer);
  943.  
  944.                 /*  Encode number of bytes read into first character    */
  945.                 /*  in line                                             */
  946.                 *outstr = ENCODE(n);
  947.  
  948.                 #ifdef  ADDCHECK
  949.                 /*  Reset checksum  */
  950.                 checksum = 0;
  951.                 #endif
  952.  
  953.                 /*  Encode data */
  954.                 outpos = 1;
  955.                 for(inpos = 0; inpos < n; inpos += 3) {
  956.                     ic1 = buffer[inpos];
  957.                     ic2 = buffer[inpos+1];
  958.                     ic3 = buffer[inpos+2];
  959.                     oc1 =  ic1 >> 2;
  960.                     oc2 = (ic1 << 4) & 0x030 | (ic2 >> 4) & 0x00F;
  961.                     oc3 = (ic2 << 2) & 0x03C | (ic3 >> 6) & 0x003;
  962.                     oc4 =     ic3    & 0x03F;
  963.                     outstr[outpos]   = ENCODE(oc1);
  964.                     outstr[outpos+1] = ENCODE(oc2);
  965.                     outstr[outpos+2] = ENCODE(oc3);
  966.                     outstr[outpos+3] = ENCODE(oc4);
  967.                     outpos += 4;
  968.                     #ifdef  ADDCHECK
  969.                     checksum = ( checksum + ic1 + ic2 + ic3 ) % CHECKSIZE;
  970.                     #endif 
  971.                }
  972.  
  973.                 /*  Add termination to output string    */
  974.                 #ifdef  ADDCHECK
  975.                 outstr[outpos]   = ENCODE(checksum);
  976.                 outpos++;
  977.                 #endif
  978.                 outstr[outpos]   = '\n';
  979.                 outstr[outpos+1] = '\0';
  980.  
  981.                 /*  Write data into archive */
  982.                 if (fputs(outstr, arc_fptr) == EOF)
  983.                     errorexit("File error writing to archive file", NOUSAGE);
  984.  
  985.                 filesize += n;
  986.  
  987.             } while (n > 0);
  988.  
  989.             /*  Write end statement   */
  990.             fprintf(arc_fptr, "end\n");
  991.             fprintf(arc_fptr, "size %d\n", filesize);
  992.  
  993.             /*  Indicate that file has been processed   */
  994.             printf("%21d - %s\n", filesize, prname(argv[targc], pathflag));
  995.  
  996.             numfiles++;
  997.     
  998.             /*  Close file  */
  999.             COND_CLOSE(file_fptr);
  1000.             file_fptr = NULL;
  1001.  
  1002.             /*  In the case of a move command, delete file  */
  1003.             if (moveflag == MOVEFILE)
  1004.                 remove(argv[targc]);      
  1005.  
  1006.         }
  1007.  
  1008.     }
  1009.  
  1010.     return numfiles;
  1011. }
  1012.  
  1013.  
  1014.  
  1015. /*  Main entry point, taking standard "main" style parameters   */
  1016.  
  1017. void main(argc, argv)
  1018. int argc;
  1019. char *argv[];
  1020. {
  1021.     char command[BUFSIZE];
  1022.     int numfiles, numdel, comlen, compos;
  1023.  
  1024.     /*  Trap program interruption, to exit cleanly      */
  1025.     /*  A 'certain' compiler gives warnings -the code   */
  1026.     /*  is correct, the compiler is wrong (sorry, but   */
  1027.     /*  according to the K&R book, and just about every */
  1028.     /*  other compiler - a signal handler is supposed   */
  1029.     /*  supposed to return void, _NOT_ int) - program   */
  1030.     /*  will still work, presumably to just ignore the  */
  1031.     /*  compiler warnings in this curcumstance is okay  */
  1032.     #ifdef SIGABRT
  1033.     signal(SIGABRT, abnormalexit);
  1034.     #endif
  1035.     #ifdef SIGINT
  1036.     signal(SIGINT, abnormalexit);
  1037.     #endif
  1038.     #ifdef SIGTERM
  1039.     signal(SIGTERM, abnormalexit);
  1040.     #endif
  1041.  
  1042.     /*  Display program title    */
  1043.     printf(TITLESTR, TITLESTRPAR);
  1044.  
  1045.     /*  If no arguments specified, display usage and exit   */
  1046.     if (argc < 2) {
  1047.         usage(stderr);
  1048.         cleanexit(EXIT_FAILURE);
  1049.     }
  1050.  
  1051.     /*  If only argument is a question-mark, display usage and exit */
  1052.     if (strcmp(argv[1], "?") == 0) {
  1053.         usage(stdout);
  1054.         cleanexit(EXIT_FAILURE);
  1055.     }
  1056.  
  1057.     /*  Check existance of an archiver command  */
  1058.     if (sscanf(argv[1], "-%s", command) != 1)
  1059.         errorexit("Archiver command missing/invalid", SHOWUSAGE);
  1060.     comlen = strlen(command);
  1061.  
  1062.     /*  Check command is right length   */
  1063.     if ((comlen < 1) || (comlen > 3))
  1064.         errorexit("Archiver command/options missing/invalid", SHOWUSAGE);
  1065.  
  1066.     /*  Deal with flags if given  */
  1067.     compos = 1;
  1068.     while (compos < comlen) {
  1069.         switch(tolower(command[compos])) {
  1070.             case PATHOPT:
  1071.                 if (pathflag == LEAVEPATH)
  1072.                     errorexit("Archiver options invalid", SHOWUSAGE);
  1073.                 pathflag = LEAVEPATH;
  1074.                 break;
  1075.             case MOVEOPT:
  1076.                 if (moveflag == MOVEFILE)
  1077.                     errorexit("Archiver options invalid", SHOWUSAGE);
  1078.                 moveflag = MOVEFILE;
  1079.                 break;
  1080.             default:
  1081.                 errorexit("Archiver options invalid", SHOWUSAGE);
  1082.         }
  1083.         compos++;
  1084.     }
  1085.  
  1086.     /*  Remove case dependancy of command   */
  1087.     *command = tolower(*command);
  1088.  
  1089.     /*  Check validity of supplied archiver command */
  1090.     if ((*command != LISTCOM) &&
  1091.         (*command != TESTCOM) &&
  1092.         (*command != EXTRACTCOM) &&
  1093.         (*command != DELETECOM) &&
  1094.         (*command != ADDCOM))
  1095.         errorexit("Invalid archiver command", SHOWUSAGE);
  1096.  
  1097.     /*  Perform required action dependant upon user supplied command    */
  1098.  
  1099.     switch(*command) {
  1100.  
  1101.         /*  In the case of an archive listing request   */
  1102.  
  1103.         case LISTCOM:
  1104.  
  1105.             /*  Check archiver options are valid for this operation */
  1106.             if ((moveflag == MOVEFILE) || (pathflag == LEAVEPATH))
  1107.                 errorexit("Archiver options invalid", SHOWUSAGE);
  1108.  
  1109.             /*  Check number of arguments   */
  1110.             if (argc != 3)
  1111.                 errorexit("Wrong number of arguments", SHOWUSAGE);
  1112.  
  1113.             /*  Open archive for input  */
  1114.             if ((arc_fptr = fopen(argv[2], "r")) == NULL)
  1115.                 errorexit("Can't open archive for input", SHOWUSAGE);
  1116.  
  1117.             /*  Print header information    */
  1118.             printf("Listing");
  1119.             pr_head(argv[2]);
  1120.  
  1121.             /*  Perform listing operation   */
  1122.             numfiles = listarc();
  1123.  
  1124.             /*  Print tail information  */
  1125.             pr_tail();
  1126.  
  1127.             /*  Give operation statistics   */
  1128.             operout("found",numfiles);
  1129.             succout("Listing", numerrors);
  1130.  
  1131.             /*  Exit program cleanly    */
  1132.             cleanexit(EXIT_SUCCESS);
  1133.  
  1134.  
  1135.         /*  In the case of an archive deletion request      */
  1136.  
  1137.         case DELETECOM:
  1138.  
  1139.             /*  Check archiver options are valid for this operation */
  1140.             if ((moveflag == MOVEFILE) || (pathflag == LEAVEPATH))
  1141.                 errorexit("Archiver options invalid", SHOWUSAGE);
  1142.  
  1143.             /*  Check number of arguments   */
  1144.             if (argc < 3)
  1145.                 errorexit("Wrong number of arguments", SHOWUSAGE);
  1146.  
  1147.             /*  Print header information    */
  1148.             printf("Deleting from");
  1149.             pr_head(argv[2]);
  1150.  
  1151.             /*  Perform deletion operation  */
  1152.             numdel = deletearc(argv[2], argv, argc);
  1153.  
  1154.             /*  Print tail information  */
  1155.             pr_tail();
  1156.  
  1157.             /*  Give operation statistics   */
  1158.             operout("deleted", numdel);
  1159.             errsout(numerrors);
  1160.             succout("Deletion", numerrors);
  1161.  
  1162.             /*  Exit program cleanly    */
  1163.             if (numerrors == 0)
  1164.                 cleanexit(EXIT_SUCCESS);
  1165.             else
  1166.                 cleanexit(EXIT_FAILURE);
  1167.  
  1168.  
  1169.         /*  In the case of an archive extraction/testing request    */
  1170.  
  1171.         case TESTCOM:
  1172.         case EXTRACTCOM:
  1173.  
  1174.             /*  Check archiver options are valid for this operation */
  1175.             if ((*command == TESTCOM) &&
  1176.                 ((moveflag == MOVEFILE) || (pathflag == LEAVEPATH)))
  1177.                 errorexit("Archiver options invalid", SHOWUSAGE);
  1178.  
  1179.             /*  Check number of arguments   */
  1180.             if ((*command == TESTCOM) && (argc != 3))
  1181.                 errorexit("Wrong number of arguments", SHOWUSAGE);
  1182.             if ((*command != TESTCOM) && (argc < 3))
  1183.                 errorexit("Not enough arguments", SHOWUSAGE);
  1184.  
  1185.             /*  Open archive for input  */
  1186.             if ((arc_fptr = fopen(argv[2], "r")) == NULL)
  1187.                 errorexit("Can't open archive for input", SHOWUSAGE);
  1188.  
  1189.             /*  Print header information    */
  1190.             if (*command == TESTCOM)
  1191.                 printf("Testing ");
  1192.             else
  1193.                 printf("Extracting from ");
  1194.             pr_head(argv[2]);
  1195.  
  1196.             /*  Perform extraction operation    */
  1197.             numfiles = extractarc(*command, argv, argc);
  1198.  
  1199.             /*  Print tail information  */
  1200.             pr_tail();
  1201.  
  1202.             /*  Give operation statistics   */
  1203.             if (*command != TESTCOM) {
  1204.                 operout("extracted", numfiles);
  1205.                 errsout(numerrors);
  1206.                 succout("Extraction", numerrors);
  1207.             }
  1208.             else {
  1209.                 operout("tested", numfiles);
  1210.                 errsout(numerrors);
  1211.                 succout("Testing", numerrors);
  1212.             }
  1213.  
  1214.             /*  In the case of a move option    */
  1215.             if (moveflag == MOVEFILE) {
  1216.  
  1217.                 /*  If there were errors in extraction, don't   */
  1218.                 /*  risk deleting from archive                  */
  1219.                 if (numerrors != 0)
  1220.                     printf("Not risking deleting from archive!\n");
  1221.  
  1222.                 /*  Otherwise, delete files that were extracted */
  1223.                 else {
  1224.  
  1225.                     /*  Close the archive file first    */
  1226.                     COND_CLOSE(arc_fptr);
  1227.                     arc_fptr = NULL;
  1228.  
  1229.                     /*  Print header information    */
  1230.                     printf("Deleting from");
  1231.                     pr_head(argv[2]);
  1232.  
  1233.                     /*  Perform deletion operation  */
  1234.                     numdel = deletearc(argv[2], argv, argc);
  1235.  
  1236.                     /*  Print tail information  */
  1237.                     pr_tail();
  1238.  
  1239.                     /*  Give operation statistics   */
  1240.                     operout("deleted", numdel);
  1241.                     errsout(numerrors);
  1242.                     succout("Deletion", numerrors);
  1243.  
  1244.                 }
  1245.  
  1246.             }
  1247.  
  1248.  
  1249.             /*  Exit program cleanly    */
  1250.             if (numerrors == 0)
  1251.                 cleanexit(EXIT_SUCCESS);
  1252.             else
  1253.                 cleanexit(EXIT_FAILURE);
  1254.  
  1255.  
  1256.         /*  In the case of an add/move to an archive request    */
  1257.  
  1258.         case ADDCOM:
  1259.  
  1260.             /*  Check number of arguments   */
  1261.             if (argc < 4)
  1262.                 errorexit("Not enough arguments", SHOWUSAGE);
  1263.  
  1264.             /*  Open archive for update  */
  1265.             if ((arc_fptr = fopen(argv[2], "a")) == NULL)
  1266.                 errorexit("Can't open archive", SHOWUSAGE);
  1267.  
  1268.             /*  Print header information    */
  1269.             if (*command == ADDCOM)
  1270.                 printf("Adding to");
  1271.             else
  1272.                 printf("Moving to");
  1273.             pr_head(argv[2]);
  1274.  
  1275.             /*  Perform add operation   */
  1276.             numfiles = addarc(argv, argc);
  1277.  
  1278.             /*  Print tail information  */
  1279.             pr_tail();
  1280.  
  1281.             /*  Give operation statistics   */
  1282.             if (*command == ADDCOM) {
  1283.                 operout("added", numfiles);
  1284.                 errsout(numerrors);
  1285.                 succout("Adding", numerrors);
  1286.             }
  1287.             else {
  1288.                 operout("moved", numfiles);
  1289.                 errsout(numerrors);
  1290.                 succout("Moving", numerrors);
  1291.             }
  1292.  
  1293.             /*  Exit program cleanly    */
  1294.             if (numerrors == 0)
  1295.                 cleanexit(EXIT_SUCCESS);
  1296.             else
  1297.                 cleanexit(EXIT_FAILURE);
  1298.  
  1299.     }
  1300.  
  1301.     /*  By logic the program should never get this far */
  1302.     errorexit("Internal error - improper program execution", NOUSAGE);
  1303. }
  1304.